package cn.centuryw.BeginToLearn.ThreadCommunication;

/**
 * 线程通信（了解原理，代码几乎不用）
 * 线程通信:多个线程因为在同一个进程中，所以互相通信比较容易的。
 * 线程通信的经典模型:生产者与消费者问题。
 * 生产者负责生成商品，消费者负责消费商品。
 * 生产不能过剩，消费不能没有。
 * 模拟一个案例:
 * 小明和小红有一个共同账户:共享资源
 * 他们有3个爸爸（亲爸，岳父，干爹）给他们存钱。
 * 模型:小明和小红去取钱，如果有钱就取出，然后等待自己，唤醒他们3个爸爸们来存钱
 * 他们的爸爸们来存钱，如果发现有钱就不存，没钱就存钱，然后等待自己，唤醒孩子们
 * 做整存整取:10000元。
 * <p>
 * 分析:
 * 生产者线程:亲爸，岳父，干爹
 * 消费者线程:小明，小红
 * 共亨资源:账户对象。
 * 注意:线程通信一定是多个线程在操作同一个资源才需要进行通信。
 * 线程通信必须先保证线程安全，否则毫无意义，代码也会报错!
 * <p>
 * 线程通信的核心方法:
 * public void wait():让当前线程进入到等待状态此方法必须锁对象调用.
 * public void notify():唤醒当前锁对象上等待状态的某个线程此方法必须锁对象调用
 * public void notifyAll() :唤醒当前锁对象上等待状态的全部线程妳此方法必须锁对象调用
 * <p>
 * <p>
 * 并发编程下，多线程访问变量的不可见性问题。
 * 引入:
 * 多个线程访问共享变量，会出现一个线程修改变量的值后，其他线程看不到变量最新值的情况。
 * 结论:
 * 并发编程下，多线程修改变量，会出现线程间变量的不可见性。
 * 不可见性的原因:
 * 每个线程都有自己的工作内存，线程都是从主内存拷贝共享变量的副本值。
 * 每个线程是在自己的工作内存中操作共享变量的。
 * <p>
 * 解决方案有两种常见方式:
 * 1.加锁
 * 某一个线程进入synchronized代码块前后，执行过程入如下:
 * a.线程获得锁
 * b.清空工作内存
 * c.从主内存拷贝共享变量最新的值到工作内存成为副本
 * d.执行代码
 * e.将修改后的副本的值刷新回主内存中
 * f.线程释放锁
 * 2.对共享的变量进行volatile关键字修饰。
 * a.子线程t从主内存读取到数据放入其对应的工作内存
 * b.将flag的值更改为true，但是这个时候flag的值还没有写会主内存
 * c.此时main方法main方法读取到了flag的值为false
 * d.当子线程t将flag的值写回去后，失效其他线程对此变量副本
 * e.再次对flag进行操作的时候线程会从主内存读取最新的值，放入到工作内存中
 * 总结: volatile保证不同线程对共享变量操作的可见性，也就是说一个线程修改了volatile修饰的变量，当修改
 * 写回主内存时，另外—个线程立即看到最新的值。
 *
 */
public class Manager {
    public static void main(String[] args) {
        // 并发编程下，多线程访问变量的不可见性问题。
        volatileDemo();


        // 1.创建共享账户
        BankAccount account = new BankAccount("ICBC-10001", 0);
        // 2.创建两个线程对象进行取钱操作
        new ThreadDraw("小明", account).start();
        new ThreadDraw("小红", account).start();
        // 3.创建两个线程对象进行存钱操作
        new ThreadSave("爸爸", account).start();
        new ThreadSave("妈妈", account).start();

    }

    /**
     * 并发编程下，多线程访问变量的不可见性问题与解决方案
     */
    static void volatileDemo() {
        // 1.启动子线程，修改flag的变量为true
        VolatileDemo t = new VolatileDemo();
        t.start();

        // 2,主线程
        /*
        while (true){
            // 解决方案1.加锁　　加锁会清空工作内存，读取主内存中的最新值到工作内存中来!
            synchronized (VolatileDemo.class){
                if (t.isFlag()){
                    System.out.println("主线程执行...");
                    break;
                }
            }
        }
        */

        // 方案2.对共享的变量进行volatile关键字修饰。
        while (true) {
            // 解决方案1.加锁　　加锁会清空工作内存，读取主内存中的最新值到工作内存中来!
            if (t.isFlag()) {
                System.out.println("主线程执行...");
                break;
            }
        }
    }


}
